package com.zhentao.util;

import com.zhentao.config.MinioConfig;
import io.minio.*;
import io.minio.errors.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.databind.ObjectMapper;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/**
 * MinIO工具类
 */
@Slf4j
@Component
public class MinioUtil {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfig minioConfig;

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd");

    /**
     * 初始化，检查并创建存储桶
     */
    @PostConstruct
    public void init() {
        try {
            // 检查并创建默认存储桶（用于人脸识别等）
            boolean bucketExists = minioClient.bucketExists(
                    BucketExistsArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .build()
            );

            if (!bucketExists) {
                minioClient.makeBucket(
                        MakeBucketArgs.builder()
                                .bucket(minioConfig.getBucketName())
                                .build()
                );
                log.info("创建MinIO存储桶成功: {}", minioConfig.getBucketName());
            } else {
                log.info("MinIO存储桶已存在: {}", minioConfig.getBucketName());
            }

            // 检查并创建医生专用存储桶
            boolean doctorBucketExists = minioClient.bucketExists(
                    BucketExistsArgs.builder()
                            .bucket(minioConfig.getDoctorBucketName())
                            .build()
            );

            if (!doctorBucketExists) {
                minioClient.makeBucket(
                        MakeBucketArgs.builder()
                                .bucket(minioConfig.getDoctorBucketName())
                                .build()
                );
                log.info("创建MinIO医生专用存储桶成功: {}", minioConfig.getDoctorBucketName());
                
                // 设置桶的公共读取策略
                setBucketPublicReadPolicy(minioConfig.getDoctorBucketName());
            } else {
                log.info("MinIO医生专用存储桶已存在: {}", minioConfig.getDoctorBucketName());
                
                // 确保桶有公共读取策略
                setBucketPublicReadPolicy(minioConfig.getDoctorBucketName());
            }
            
            // 测试配置是否正确
            testMinioConfiguration();

        } catch (Exception e) {
            log.error("MinIO初始化失败", e);
            throw new RuntimeException("MinIO初始化失败", e);
        }
    }

    /**
     * 上传人脸图片
     *
     * @param file 图片文件
     * @return 图片访问URL
     */
    public String uploadFaceImage(MultipartFile file) {
        try {
            // 生成对象名称（按日期分组）
            String dateFolder = DATE_FORMAT.format(new Date());
            String originalFilename = file.getOriginalFilename();
            String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            String filename = System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8) + extension;
            String objectName = "face/" + dateFolder + "/" + filename;

            // 上传文件
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(objectName)
                            .stream(file.getInputStream(), file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build()
            );

            // 返回访问URL
            String imageUrl = minioConfig.getUrlPrefix() + objectName;
            log.info("人脸图片上传成功: {}", imageUrl);
            return imageUrl;

        } catch (Exception e) {
            log.error("上传人脸图片失败", e);
            throw new RuntimeException("上传人脸图片失败", e);
        }
    }

    /**
     * 上传医生头像
     *
     * @param file 头像文件
     * @param doctorId 医生ID
     * @return 头像访问URL
     */
    public String uploadDoctorAvatar(MultipartFile file, Integer doctorId) {
        try {
            // 验证文件类型
            String contentType = file.getContentType();
            if (contentType == null || !contentType.startsWith("image/")) {
                throw new RuntimeException("只能上传图片文件");
            }

            // 验证文件大小（最大5MB）
            long maxSize = 5 * 1024 * 1024; // 5MB
            if (file.getSize() > maxSize) {
                throw new RuntimeException("头像文件大小不能超过5MB");
            }

            // 生成对象名称（按医生ID分组）
            String dateFolder = DATE_FORMAT.format(new Date());
            String originalFilename = file.getOriginalFilename();
            String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            String filename = "doctor_" + doctorId + "_" + System.currentTimeMillis() + extension;
            String objectName = "avatar/" + dateFolder + "/" + filename;

            // 上传文件到医生专用桶
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(minioConfig.getDoctorBucketName())
                            .object(objectName)
                            .stream(file.getInputStream(), file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build()
            );

            // 返回访问URL
            String avatarUrl = minioConfig.getDoctorUrlPrefix() + objectName;
            log.info("医生头像上传成功: doctorId={}, objectName={}, url={}", doctorId, objectName, avatarUrl);
            log.info("MinIO配置信息: endpoint={}, doctorBucket={}, doctorUrlPrefix={}", 
                    minioConfig.getEndpoint(), minioConfig.getDoctorBucketName(), minioConfig.getDoctorUrlPrefix());
            
            // 验证上传的文件是否可访问
            testImageAccess(avatarUrl);
            
            return avatarUrl;

        } catch (Exception e) {
            log.error("上传医生头像失败: doctorId={}", doctorId, e);
            throw new RuntimeException("上传医生头像失败: " + e.getMessage(), e);
        }
    }

    /**
     * 删除对象
     *
     * @param objectName 对象名称
     */
    public void deleteObject(String objectName) {
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(objectName)
                            .build()
            );
            log.info("删除对象成功: {}", objectName);

        } catch (Exception e) {
            log.error("删除对象失败: {}", objectName, e);
            throw new RuntimeException("删除对象失败", e);
        }
    }

    /**
     * 获取对象输入流
     *
     * @param objectName 对象名称
     * @return 输入流
     */
    public InputStream getObject(String objectName) {
        try {
            return minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(objectName)
                            .build()
            );

        } catch (Exception e) {
            log.error("获取对象失败: {}", objectName, e);
            throw new RuntimeException("获取对象失败", e);
        }
    }

    /**
     * 检查对象是否存在
     *
     * @param objectName 对象名称
     * @return true：存在，false：不存在
     */
    public boolean objectExists(String objectName) {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(objectName)
                            .build()
            );
            return true;

        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 根据URL提取对象名称
     *
     * @param url 完整URL
     * @return 对象名称
     */
    public String extractObjectName(String url) {
        if (url != null) {
            // 优先匹配医生专用URL前缀
            if (url.startsWith(minioConfig.getDoctorUrlPrefix())) {
                return url.substring(minioConfig.getDoctorUrlPrefix().length());
            }
            // 匹配默认URL前缀
            if (url.startsWith(minioConfig.getUrlPrefix())) {
                return url.substring(minioConfig.getUrlPrefix().length());
            }
        }
        return null;
    }

    /**
     * 根据URL删除对象（自动识别桶）
     *
     * @param url 完整URL
     */
    public void deleteObjectByUrl(String url) {
        try {
            if (url != null) {
                String objectName = null;
                String bucketName = null;
                
                // 判断是医生专用桶还是默认桶
                if (url.startsWith(minioConfig.getDoctorUrlPrefix())) {
                    objectName = url.substring(minioConfig.getDoctorUrlPrefix().length());
                    bucketName = minioConfig.getDoctorBucketName();
                } else if (url.startsWith(minioConfig.getUrlPrefix())) {
                    objectName = url.substring(minioConfig.getUrlPrefix().length());
                    bucketName = minioConfig.getBucketName();
                }
                
                if (objectName != null && bucketName != null) {
                    minioClient.removeObject(
                            RemoveObjectArgs.builder()
                                    .bucket(bucketName)
                                    .object(objectName)
                                    .build()
                    );
                    log.info("删除对象成功: bucket={}, object={}", bucketName, objectName);
                }
            }

        } catch (Exception e) {
            log.error("删除对象失败: {}", url, e);
            throw new RuntimeException("删除对象失败", e);
        }
    }

    /**
     * 设置桶的公共读取策略
     *
     * @param bucketName 桶名称
     */
    private void setBucketPublicReadPolicy(String bucketName) {
        try {
            // 公共读取策略JSON
            String publicReadPolicy = "{\n" +
                "  \"Version\": \"2012-10-17\",\n" +
                "  \"Statement\": [\n" +
                "    {\n" +
                "      \"Effect\": \"Allow\",\n" +
                "      \"Principal\": {\n" +
                "        \"AWS\": \"*\"\n" +
                "      },\n" +
                "      \"Action\": \"s3:GetObject\",\n" +
                "      \"Resource\": \"arn:aws:s3:::" + bucketName + "/*\"\n" +
                "    }\n" +
                "  ]\n" +
                "}";

            minioClient.setBucketPolicy(
                    SetBucketPolicyArgs.builder()
                            .bucket(bucketName)
                            .config(publicReadPolicy)
                            .build()
            );
            
            log.info("成功设置桶的公共读取策略: {}", bucketName);
            
        } catch (Exception e) {
            log.error("设置桶公共读取策略失败: {}", bucketName, e);
            // 不抛出异常，避免影响主流程
        }
    }

    /**
     * 测试图片URL是否可以访问
     *
     * @param imageUrl 图片URL
     */
    private void testImageAccess(String imageUrl) {
        try {
            URL url = new URL(imageUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("HEAD");
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(5000);
            
            int responseCode = connection.getResponseCode();
            if (responseCode == 200) {
                log.info("头像URL可以正常访问: {}", imageUrl);
            } else {
                log.warn("头像URL访问异常: {}, 响应码: {}", imageUrl, responseCode);
            }
            
            connection.disconnect();
            
        } catch (Exception e) {
            log.error("测试头像URL访问失败: {}", imageUrl, e);
        }
    }

    /**
     * 测试MinIO配置是否正确
     */
    private void testMinioConfiguration() {
        try {
            log.info("=== MinIO配置测试开始 ===");
            log.info("Endpoint: {}", minioConfig.getEndpoint());
            log.info("Access Key: {}", minioConfig.getAccessKey());
            log.info("Doctor Bucket: {}", minioConfig.getDoctorBucketName());
            log.info("Doctor URL Prefix: {}", minioConfig.getDoctorUrlPrefix());
            
            // 测试桶是否存在
            boolean exists = minioClient.bucketExists(
                    BucketExistsArgs.builder()
                            .bucket(minioConfig.getDoctorBucketName())
                            .build()
            );
            log.info("Doctor桶是否存在: {}", exists);
            
            // 获取桶策略
            try {
                String policy = minioClient.getBucketPolicy(
                        GetBucketPolicyArgs.builder()
                                .bucket(minioConfig.getDoctorBucketName())
                                .build()
                );
                log.info("Doctor桶策略: {}", policy != null ? "已设置" : "未设置");
            } catch (Exception e) {
                log.warn("获取桶策略失败: {}", e.getMessage());
            }
            
            log.info("=== MinIO配置测试完成 ===");
            
        } catch (Exception e) {
            log.error("MinIO配置测试失败", e);
        }
    }
}
